using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.BizTalk.Message.Interop;
using Sixeyed.CacheAdapter.Interfaces;
using System.Xml;

namespace Sixeyed.CacheAdapter.Configuration
{
    /// <summary>
    /// Represents properties for an object which may be cached
    /// </summary>
    /// <remarks>
    /// Holds all the context properties set in BizTalk for a message which may 
    /// be cached; also used to store cache parameters for a message in the cache
    /// </remarks>
    [Serializable]
    public class MessageCacheProperties
    {
        private bool _isMessageCacheEnabled;
        private string _messageCacheKey;
        private TimeSpan _messageCacheLifespan;
        private bool _isCached;
        private string _interchangeId;

        /// <summary>
        /// Gets/sets whether the cache is enabled for the object
        /// </summary>
        public bool IsMessageCacheEnabled
        {
            get { return this._isMessageCacheEnabled; }
            set{ this._isMessageCacheEnabled = value; }
        }
        
        /// <summary>
        /// Gets/sets the cache key to use for the object
        /// </summary>
        public string MessageCacheKey
        {
            get { return this._messageCacheKey; }
            set { this._messageCacheKey = value; }
        }
        
        /// <summary>
        /// Gets/sets the cache lifespan for the object
        /// </summary>
        public TimeSpan MessageCacheLifespan
        {
            get { return this._messageCacheLifespan; }
            set { this._messageCacheLifespan = value; }
        }

        /// <summary>
        /// Gets/sets whether the object is in the cache
        /// </summary>
        public bool IsCached
        {
            get { return this._isCached; }
            set { this._isCached = value; }
        }

        /// <summary>
        /// Gets.sets the interchange id of the message
        /// </summary>
        public string InterchangeId
        {
            get { return this._interchangeId; }
            set { this._interchangeId = value; }
        }

        /// <summary>
        /// Returns the cache properties from the message context
        /// </summary>
        /// <param name="message">Request message</param>
        /// <returns>Cache properties</returns>
        public static MessageCacheProperties LoadFromMessage(IBaseMessage message)
        {
            //load the cache configuration values from context:
            MessageCacheProperties contextProperties = new MessageCacheProperties();
            object value = message.Context.Read(PropertyName.IsMessageCacheEnabled, Namespace.CacheAdapter);
            if (value != null)
            {
                contextProperties.IsMessageCacheEnabled = (bool)value;
            }
            value = message.Context.Read(PropertyName.MessageCacheKey, Namespace.CacheAdapter);
            if (value != null)
            {
                contextProperties.MessageCacheKey = (string)value;
            }
            contextProperties.MessageCacheLifespan = TimeSpan.Zero;
            value = message.Context.Read(PropertyName.MessageCacheLifespan, Namespace.CacheAdapter);
            if (value != null && value is string)
            {
                contextProperties.MessageCacheLifespan = XmlConvert.ToTimeSpan(value.ToString());
            }

            contextProperties.InterchangeId = GetInterchangeId(message);
            return contextProperties;
        }

        /// <summary>
        /// Gets the cached message cache properties for an interchange
        /// </summary>
        /// <remarks>
        /// Used to retrieve cache properties for a service response
        /// where the context properties are not available, but have been 
        /// cached at the point the service was requested
        /// </remarks>
        /// <param name="provider">Cache provider</param>
        /// <param name="message">Request message</param>
        /// <returns>Cache properties</returns>
        public static MessageCacheProperties LoadFromCache(ICacheProvider provider, IBaseMessage message)
        {
            //load the message cache configuration values from the cache:
            MessageCacheProperties cachedProperties = new MessageCacheProperties();
            string interchangeId = GetInterchangeId(message);
            if (!string.IsNullOrEmpty(interchangeId))
            {
                cachedProperties = provider.Get<MessageCacheProperties>(interchangeId);
                if (cachedProperties == null)
                {
                    cachedProperties = new MessageCacheProperties();
                }
            }
            return cachedProperties;
        }

        /// <summary>
        /// Updates message context with cache properties, adding to cache using the 
        /// interchange id as the key, if response not already cached
        /// </summary>
        /// <param name="message">Message</param>
        /// <param name="contextProperties">Cache properties</param>
        /// <param name="provider">Cache provider</param>
        public static void Save(IBaseMessage message, MessageCacheProperties contextProperties, ICacheProvider provider)
        {
            //set context properties:
            message.Context.Promote(PropertyName.IsMessageCacheEnabled, Namespace.CacheAdapter, contextProperties.IsMessageCacheEnabled);
            message.Context.Write(PropertyName.MessageCacheKey, Namespace.CacheAdapter, contextProperties.MessageCacheKey);
            message.Context.Write(PropertyName.MessageCacheLifespan, Namespace.CacheAdapter, XmlConvert.ToString(contextProperties.MessageCacheLifespan));
            message.Context.Promote(PropertyName.IsCached, Namespace.CacheAdapter, contextProperties.IsCached);

            //and if the response is not in the cache, add properties to the cache 
            //based on interchange id for later lookup:
            if (!contextProperties.IsCached && !string.IsNullOrEmpty(contextProperties.InterchangeId))
            {
                provider.Put(contextProperties.InterchangeId, contextProperties);
            }
        }

        /// <summary>
        /// Returns the interchange ID for the given message
        /// </summary>
        /// <param name="message">Message</param>
        /// <returns>Interchange ID</returns>
        private static string GetInterchangeId(IBaseMessage message)
        {
            string interchangeId = string.Empty;
            object value = message.Context.Read(PropertyName.InterchangeID, Namespace.SystemProperties);
            if (value != null)
            {
                interchangeId = value.ToString();
            }
            return interchangeId;
        }

        /// <summary>
        /// Namespaces used in the serialized <see cref="MessageCacheProperties"/> object
        /// </summary>
        public struct Namespace
        {
            /// <summary>
            /// "http://schemas.sixeyed.com/CacheAdapter/2009"
            /// </summary>
            public const string CacheAdapter = "http://schemas.sixeyed.com/CacheAdapter/2009";

            /// <summary>
            /// "http://schemas.microsoft.com/BizTalk/2003/system-properties"
            /// </summary>
            public const string SystemProperties = "http://schemas.microsoft.com/BizTalk/2003/system-properties";
        }

        /// <summary>
        /// Properties used in the serialized <see cref="MessageCacheProperties"/> object
        /// </summary>
        public struct PropertyName
        {
            /// <summary>
            /// "IsMessageCacheEnabled"
            /// </summary>
            public const string IsMessageCacheEnabled = "IsMessageCacheEnabled";

            /// <summary>
            /// "MessageCacheKey"
            /// </summary>
            public const string MessageCacheKey = "MessageCacheKey";

            /// <summary>
            /// "MessageCacheLifespan"
            /// </summary>
            public const string MessageCacheLifespan = "MessageCacheLifespan";

            /// <summary>
            /// "IsCached"
            /// </summary>
            public const string IsCached = "IsCached";

            /// <summary>
            /// "CacheProviderTypeName"
            /// </summary>
            public const string CacheProviderTypeName = "CacheProviderTypeName";

            /// <summary>
            /// "CacheProviderConfiguration"
            /// </summary>
            public const string CacheProviderConfiguration = "CacheProviderConfiguration";

            /// <summary>
            /// "InterchangeID"
            /// </summary>
            public const string InterchangeID = "InterchangeID";
        }
    }
}
